#include "common.hpp"

static const char *optString = "r:i:h?";

static const struct option longOpts[] = {
	{ "help", no_argument, NULL, 'h' },
	{ "index", required_argument, NULL, 'i' },
	{ "resource", required_argument, NULL, 'r' },
	{ 0,0,0,0 }
};

int main (int argc, char* const argv[]) {
	srand( time(NULL) + getpid() );

	ipc::messages_queue_with_exceptions repositor_salida ( my_ftok(MSG_QUEUE|REPOSITOR) );
	ipc::messages_queue_with_exceptions canal_sync ( my_ftok(MSG_QUEUE|SYNC) );

	int longIndex = 0;
	int option = 0;
	bool ayuda = false;
	bool seguir = true;
	bool llenadora_demorada = false;
	size_t procesadas = 0;
	long azucar = 0;
	int boca = -1;

	// Interpretación de parametros.
	while ( (option = getopt_long(argc,argv,optString,longOpts,&longIndex) ) != EOF ) {
		switch (option) {
			case '?':
			case 'h':
			case ':':
				ayuda = true;
				break;
			case 'i':
				boca = parse<size_t>(optarg,-1);
				if (boca >= MAX_LLENADORAS)
					boca = -1;
				break;
			case 'r':
				azucar = parse<long>(optarg,-1);
			default:
				break;
		}
	}

	if ( ayuda || boca < 0 || azucar < 0) {
		std::cerr << "Proceso de control de un brazo con bocas de llenado de azucar." << std::endl;
		std::cerr << "\tUso: " << argv[0] << " [-h] <-index natural> [-r natural]" << std::endl;
		std::cerr << "\tParametros:" << std::endl;
		std::cerr << "\t\t-h,--help:\tMostrar este mensaje de ayuda." << std::endl;
		std::cerr << "\t\t-i,--index:\tNúmero se semaforo a utilizar." << std::endl;
		std::cerr << "\t\t-r,--resource:\tNúmero de dosis de azucar disponibles en la estación." << std::endl;

		return (ayuda) ? 0 : -1;
	}

	// Recien en este momento se que boca de la llenadora utilizo.
	ipc::semaphore_with_exceptions mutex_llenadora (my_ftok(MUTEX|LLENADORA_0),boca);
	ipc::semaphore_with_exceptions mutex_azucaradora (my_ftok(MUTEX|AZUCARADORA_0),boca);

	sPedido pedir_azucar;
	pedir_azucar.tipo = PEDIR_RECURSO;
	pedir_azucar.origen = getpid();
	pedir_azucar.recurso = AZUCAR;
	pedir_azucar.sem_key = my_ftok(MUTEX|AZUCARADORA_0);
	pedir_azucar.sem_num = boca;

	sReposicion reposicion;
	sToken token;

	const std::string msg_header = "Estación de azucarado "+ parse(boca) + ": ";
	logger.setColor("\033[0;32m");
	try {
		repositor_salida.get(PERMITS);

		mutex_llenadora.get(PERMITS);
		mutex_azucaradora.get(PERMITS);
//		mutex_sync.get(PERMITS);

		canal_sync.get(PERMITS);

		logger << msg_header << "Inicio." << nl;
		procesadas = 0;
		while (seguir) {
			mutex_azucaradora.wait();

			// Solicitud de reposición.
			if (azucar <= 0) {
				logger << msg_header << "Enviando pedido de reposición de azucar." << nl;
				repositor_salida.send(pedir_azucar);
				logger << msg_header << "Detenida a la espera de dosis de azucar." << nl;
				mutex_azucaradora.wait();
				reposicion.tipo = 0;
				repositor_salida.receive(reposicion,getpid());
				
				if ( (reposicion.tipo == getpid()) && (reposicion.recurso=AZUCAR) ) {
					azucar+= reposicion.cantidad;
					if (reposicion.cantidad > 0)
						logger << msg_header << "Se recibio un pack de " << reposicion.cantidad << " dosis de azucar." << nl;
					else
						logger << msg_header << "Se recibio un pack sin azucar." << nl;
				} else {
					error << msg_header << "Ignorando respuesta de reposición incorrecta." << nl;
				}
			} // Fin de solicitud de reposición.
			
			if (azucar > 0) {
				logger << msg_header << "Sincronizando con llenadora." << nl;
				// Preprotocolo de sincronización con la llenadora.
				// Solo un proceso obtiene el token.
//				mutex_sync.wait();
				canal_sync.receive(token,1+boca);
				llenadora_demorada = (token.esperando == 0);
				token.esperando = (llenadora_demorada) ? 1 : 0;
				canal_sync.send(token);
//				mutex_sync.signal();

				if (llenadora_demorada) {
					logger << msg_header << "Esperando a la llenadora asociada." << nl;
					mutex_azucaradora.wait();
				} else {
					logger << msg_header << "Despertando a la llenadora asociada." << nl;
					mutex_llenadora.signal();
				}
				// Fin preprotocolo.

				azucar--;
				procesadas++;
				logger << msg_header << "Llenando botella. (" << procesadas << " en esta estación)." << nl;
				// llenar botella.
				SIMULAR_LLENAR;

				// Postprotocolo de sincronización con la azucaradora.
				// Solo un proceso obtiene el token.
//				mutex_sync.wait();
				canal_sync.receive(token,1+boca);
				llenadora_demorada = (token.esperando == 0);
				token.esperando = (llenadora_demorada) ? 1 : 0;
				canal_sync.send(token);
//				mutex_sync.signal();

				if (llenadora_demorada) {
					logger << msg_header << "Esperando a la llenadora asociada." << nl;
					mutex_azucaradora.wait();
				} else {
					logger << msg_header << "Despertando a la llenadora asociada." << nl;
					mutex_llenadora.signal();
				}
				// Fin postprotocolo.
				logger << msg_header << "Botella llenada. (" << procesadas << " en esta estación)." << nl;
				mutex_azucaradora.signal();
			} else {
				logger << msg_header << "Sin azucar." << nl;
			}
		}

	} catch (ipc::exception& e) {
		logger << msg_header << e.what() << "." << nl;
		exit(-1);
	}
	logger << msg_header << "Fin." << nl;
	return 0;
}
